﻿using System;
using System.Collections.Generic;
using Pogo.Data;
using RestSharp;
using RestSharp.Deserializers;

namespace Pogo
{
   internal class RestService
   {
      private const string ServiceURL = "http://service.pogoplug.com/svc/api/json";
      protected static readonly JsonDeserializer deserializer;
      private static readonly Dictionary<string, List<string>> deviceCommands;
      private static readonly Dictionary<string, List<string>> serviceCommands;
      protected static readonly Dictionary<string, List<string>> options; 
      private readonly RestClient client;

      static RestService()
      {
         deserializer = new JsonDeserializer();

         deviceCommands = new Dictionary<string, List<string>>();
         deviceCommands.Add("xcode", new List<string>{"listQueue"});

         serviceCommands = new Dictionary<string, List<string>>();
         serviceCommands.Add("xcode", new List<string> { "getMode" });

         options = new Dictionary<string, List<string>>();
         options.Add("music_def", new List<string> { "cesearch_artists", "cesearch_albums", "cesearch_genres", "cesearch_audio" });
      }

      internal RestService() : this(ServiceURL)
      {
      }

      internal RestService(string serviceUrl)
      {
         if (string.IsNullOrEmpty(serviceUrl)) serviceUrl = ServiceURL;
         client = new RestClient(serviceUrl);
         client.AddHandler("application/x-javascript", deserializer);
      }

      internal static IDictionary<string, List<string>> DeviceCommands
      {
         get { return deviceCommands; }
      }

      internal static IDictionary<string, List<string>> ServiceCommands
      {
         get { return serviceCommands; }
      }

      protected static void ExceptionCheck(IRestResponse response)
      {
         if (response.ErrorException != null)
            throw response.ErrorException;

         if (response.Content.Contains("HB-EXCEPTION"))
         {
            response.Content = response.Content.Replace('\'', '"');
            throw new PogoException(deserializer.Deserialize<PogoExceptionData>(response).Message);
         }

         if (response.ResponseStatus != ResponseStatus.Completed)
         {
            throw new PogoException(string.Format("Command {0} not completed. Status code: {1}",
                                                  response.Request.Resource, response.StatusCode));
         }
      }

      internal T GetUser<T>(string authToken) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");

         RestRequest request = new RestRequest("getUser") {OnBeforeDeserialization = ExceptionCheck};
         request.AddParameter("valtoken", authToken);
         IRestResponse<T> response = client.Execute<T>(request);

         return (response == null ? null : response.Data);
      }

      internal T UpdateUser<T>(string authToken, string newScreenName) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(newScreenName)) throw new ArgumentNullException("newScreenName");

         RestRequest request = new RestRequest("updateUser") { OnBeforeDeserialization = ExceptionCheck };
         request.AddParameter("valtoken", authToken);
         request.AddParameter("screenname", newScreenName);
         IRestResponse<T> response = client.Execute<T>(request);

         return response.Data;
      }

      internal T AddUserEmail<T>(string authToken, string newEmail) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(newEmail)) throw new ArgumentNullException("newEmail");

         RestRequest request = new RestRequest("addUserEmail") { OnBeforeDeserialization = ExceptionCheck };
         request.AddParameter("valtoken", authToken);
         request.AddParameter("email", newEmail);
         IRestResponse<T> response = client.Execute<T>(request);

         return response.Data;
      }

      internal T RemoveUserEmail<T>(string authToken, string email) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(email)) throw new ArgumentNullException("email");

         RestRequest request = new RestRequest("removeUserEmail") { OnBeforeDeserialization = ExceptionCheck };
         request.AddParameter("valtoken", authToken);
         request.AddParameter("email", email);
         IRestResponse<T> response = client.Execute<T>(request);

         return response.Data;
      }

      internal T GetVersion<T>() where T : class, new()
      {
         RestRequest request = new RestRequest("getVersion") {OnBeforeDeserialization = ExceptionCheck};
         IRestResponse<T> response = client.Execute<T>(request);

         return (response == null ? null : response.Data);
      }

      internal T ListDevices<T>(string authToken) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");

         RestRequest request = new RestRequest("listDevices")
                                  {RootElement = "devices", OnBeforeDeserialization = ExceptionCheck};
         request.AddParameter("valtoken", authToken);
         IRestResponse<T> response = client.Execute<T>(request);

         return (response == null ? null : response.Data);
      }

      internal T UpdateService<T>(string authToken, string newName) where T : class, new()
      {
         return null;
      }

      internal T LoginUser<T>(string email, string password) where T : class, new()
      {
         if (string.IsNullOrEmpty(email)) throw new ArgumentNullException("email");
         if (string.IsNullOrEmpty(password)) throw new ArgumentNullException("password");

         RestRequest request = new RestRequest("loginUser") {OnBeforeDeserialization = ExceptionCheck};
         request.AddParameter("email", email);
         request.AddParameter("password", password);
         IRestResponse<T> response = client.Execute<T>(request);

         return (response == null ? null : response.Data);
      }

      internal T SearchFiles<T>(string authToken, string deviceID, string serviceID, string searchCriteria, int pageOffset = 0, int maxCount = 0, string sortCriteria = null)
      {
         throw new NotImplementedException();

         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(deviceID)) throw new ArgumentNullException("deviceID");
         if (string.IsNullOrEmpty(serviceID)) throw new ArgumentNullException("serviceID");
         if (string.IsNullOrEmpty(searchCriteria)) throw new ArgumentNullException("searchCriteria");

         RestRequest request = new RestRequest("searchFiles") { OnBeforeDeserialization = ExceptionCheck };

         request.AddParameter("valtoken", authToken);
         request.AddParameter("deviceid", deviceID);
         request.AddParameter("serviceid", serviceID);
         request.AddParameter("searchCriteria", searchCriteria);
      }

      internal T ListFiles<T>(string authToken, string deviceID, string serviceID, string parentID = null,
                              bool showHidden = false, int pageOffset = 0, int maxCount = 0, string sortCriteria = null)
         where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(deviceID)) throw new ArgumentNullException("deviceID");
         if (string.IsNullOrEmpty(serviceID)) throw new ArgumentNullException("serviceID");

         RestRequest request = new RestRequest("listFiles") {OnBeforeDeserialization = ExceptionCheck};

         request.AddParameter("valtoken", authToken);
         request.AddParameter("deviceid", deviceID);
         request.AddParameter("serviceid", serviceID);

         if (showHidden) request.AddParameter("searchcrit", 1);
         if (pageOffset > 0) request.AddParameter("pageoffset", pageOffset);
         if (maxCount > 0) request.AddParameter("maxcount", maxCount);
         if (!string.IsNullOrEmpty(sortCriteria)) request.AddParameter("sortcrit", sortCriteria);
         if (!string.IsNullOrEmpty(parentID)) request.AddParameter("parentid", parentID);

         IRestResponse<T> response = client.Execute<T>(request);

         return (response == null ? null : response.Data);
      }

      internal T GetFile<T>(string authToken, string deviceID, string serviceID, string identifierType,
                            string identifier) where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(deviceID)) throw new ArgumentNullException("deviceID");
         if (string.IsNullOrEmpty(serviceID)) throw new ArgumentNullException("serviceID");
         if (string.IsNullOrEmpty(identifierType)) throw new ArgumentNullException("identifierType");
         if (string.IsNullOrEmpty(identifier)) throw new ArgumentNullException("identifier");

         if (identifierType != "path" && identifierType != "fileid")
            throw new ArgumentOutOfRangeException("identifierType", "Must be 'path' or 'fileid'");

         RestRequest request = new RestRequest("getFile") {OnBeforeDeserialization = ExceptionCheck};

         request.AddParameter("valtoken", authToken);
         request.AddParameter("deviceid", deviceID);
         request.AddParameter("serviceid", serviceID);
         request.AddParameter(identifierType, identifier);

         IRestResponse<T> response = client.Execute<T>(request);
         return response.Data;
      }

      internal T FeatureCommand<T>(string authToken, string deviceID, string serviceID, string feature, string command)
         where T : class, new()
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(deviceID)) throw new ArgumentNullException("deviceID");
         if (string.IsNullOrEmpty(feature)) throw new ArgumentNullException("feature");
         if (string.IsNullOrEmpty(command)) throw new ArgumentNullException("command");

         Dictionary<string, List<string>> commandCache = (string.IsNullOrEmpty(serviceID)
                                                             ? deviceCommands
                                                             : serviceCommands);
         if (!commandCache.ContainsKey(feature))
            throw new ArgumentException("Unknown feature", "feature");
         if (!commandCache[feature].Contains(command))
            throw new ArgumentException("Unknown command for that feature", "command");

         RestRequest request = new RestRequest("getFile") {OnBeforeDeserialization = ExceptionCheck};

         request.AddParameter("valtoken", authToken);
         request.AddParameter("deviceid", deviceID);
         if (!string.IsNullOrEmpty(serviceID)) request.AddParameter("serviceid", serviceID);
         request.AddParameter("feature", feature);
         request.AddParameter("command", command);

         IRestResponse<T> response = client.Execute<T>(request);
         return response.Data;
      }

      internal IEnumerable<string> ListFeatures(string authToken, string deviceID)
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(deviceID)) throw new ArgumentNullException("deviceID");

         RestRequest request = new RestRequest("listFeatures")
                                  {OnBeforeDeserialization = ExceptionCheck, RootElement = "features"};

         request.AddParameter("valtoken", authToken);
         request.AddParameter("deviceid", deviceID);

         IRestResponse<List<string>> response = client.Execute<List<string>>(request);
         return response.Data;
      }

      internal void SetOption(string authToken, string name, string value)
      {
         if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException("authToken");
         if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
         if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("value");
         //<soapenv:Envelope><soapenv:Body><setOption><name>music_def</name><value>cesearch_albums</value></setOption></soapenv:Body></soapenv:Envelope>
      }
   }


}

namespace Pogo.Data
{
   public enum MediaType
   {
      Audio,
      Video,
      Image
   }

   public enum BooleanOperator
   {
      Contains,
      GTE,
      LTE,
      EQ
   }

   public enum FileSearchFields
   {
      Name,
      CreationDate,
      MediaType
   }


}

